/*
 * Author: Yuan Li
 * Course: 15121 lab1
 * Last Modified Date: June/29/2011
 */

import java.io.*;


public class BMPManipulator
{
	
	/*
	 * Some useful constants -- use them in your code to maintain good style
	 */
	
	/* The number of bytes in a BMP image header */
	protected static final int HEADER_SIZE = 54;
	
	/* Width and height are stored in the image header as unsigned integers
	 * of 4 bytes starting at these indices (starts with LSB): */
	protected static final int WIDTH_BYTE  = 18; /* Image width  */
	protected static final int HEIGHT_BYTE = 22; /* Image height */
	/* The number of bytes used by an integer */
	protected static final int INT_SIZE = 4;
		
	/*
	 * Instance variables (add more and document if necessary)
	 */
	
	/* The bytes of the image header */
	protected byte header[];
	/* The bytes representing the RGB values of the image pixels */
	protected byte bytes[];
	/* The width and height of the image */
	protected int width, height;
	
	
	
	public static void main(void){
		BMPManipulator bmpm = new BMPManipulator();
		
		System.out.println("width = " + bmpm.width);
		System.out.println("height = " + bmpm.height);
	}
	
	/**
	 * Initialize the image manipulator, and read the specified image
	 * @param file The bitmap file to read
	 */
	public BMPManipulator(String file)
	{
		if (!readImage("guna.bmp"))
			throw new RuntimeException("Fail to read the image file!!");
	}
	
	/**
	 * readImage -- read the specified image and populate the header,
	 *   bytes, width, and height variables
	 * @param file The image file to read
	 * @return true if reading the image was successful, false otherwise
	 */
	public boolean readImage(String file)
	{
		try{
			
			FileInputStream inStream = new FileInputStream(file);
			byte[] arrayBuffer = new byte[1000];
			
			int max;
			if ( (max = inStream.read(arrayBuffer)) = -1){
				System.err.println("It is a empty file!!!")
				return false;
			}
			
			// extract header from the arrayBuffer
			header = new byte[54];
			
			for(int i=0; i<54; i++){
				header[i] = arrayBuffer[i];
			}
			
			// get width 
			width =  byteToInt(header,WIDTH_BYTE);
			
			// get height
			height = byteToInt(header,HEIGHT_BYTE);
			
			//extract pixel array from the arrayBuffer
			for (int i=54,j=0; i<max; i++,j++){
				bytes[j] = arrayBuffer[i];
			}
			
			return true;
			
		}catch (IOException e){
			System.err.println(e);
		}catch (FileNotFoundExcepiton e){
			System.err.println(e);
		}catch (NullPointException e){
			System.err.println(e);
		}catch (Excepiton e){
			System.err.println(e);
		}
		
		
		return false;
		
		
		
	
		throw new RuntimeException("You must implement this method!");
	}
	
	/**
	 * byteToInt -- convert a contious 4 bytes in a byte array into a int value
	 * (also from little endian to big endian)
	 * @param b the source byte array
	 * @param index the index of the first byte chunk out of 4 
	 * @return result of the integer value
	 */
	protect int byteToInt(const byte[] b,int index){
		int combination = (int) (((byte[index+3]&0x000000FF)<<24) + ((byte[index+2]&0x000000FF)<<16) + ((byte[index+1]&0x000000FF)<<8)) + byte[index]);
		
		return combination;
	}
	
	/**
	 * writeImage -- write the stored image to the specified file
	 * @param file The output image file
	 * @return true if writing the image was successful, false otherwise
	 */
	public boolean writeImage(String file)
	{
		throw new RuntimeException("You must implement this method!");
	}
	
	/**
	 * indexRed -- return the index into the byte array representing
	 *   the red value of pixel at row r and column c
	 * @param r The desired row (row 0 is the TOP row)
	 * @param c The desired column
	 * @return the red value of pixel (r, c)
	 */
	public int indexRed(int r, int c)
	{
		throw new RuntimeException("You must implement this method!");
	}
	
	/**
	 * indexGreen -- return the index into the byte array representing
	 *   the green value of pixel at row r and column c
	 * @param r The desired row (row 0 is the TOP row)
	 * @param c The desired column
	 * @return the green value of pixel (r, c)
	 */
	public int indexGreen(int r, int c)
	{
		throw new RuntimeException("You must implement this method!");
	}
	
	/**
	 * indexBlue -- return the index into the byte array representing
	 *   the blue value of pixel at row r and column c
	 * @param r The desired row (row 0 is the TOP row)
	 * @param c The desired column
	 * @return the blue value of pixel (r, c)
	 */
	public int indexBlue(int r, int c)
	{
		throw new RuntimeException("You must implement this method!");
	}
	
	/**
	 * removeRed -- remove all of the red from the stored image
	 */
	public void removeRed()
	{
		throw new RuntimeException("You must implement this method!");
	}
	
	/**
	 * flip -- vertically flip (reflect) the stored image 
	 */
	public void flip()
	{
		throw new RuntimeException("You must implement this method!");
	}
	
	/**
	 * rotate -- rotate a square image 90 degrees clockwise
	 *   Do nothing if the image is not square
	 */
	public void rotate()
	{
		throw new RuntimeException("You must implement this method!");
	}
	
	/**
	 * getWidth -- get the width of the current image
	 * DO NOT MODIFY THIS METHOD -- it will be used for grading
	 * @return the width of the image
	 */
	public int getWidth()
	{
		return width;
	}
	
	/**
	 * getHeight -- get the height of the current image
	 * DO NOT MODIFY THIS METHOD -- it will be used for grading
	 * @return the height of the image
	 */
	public int getHeight()
	{
		return height;
	}
	
	/**
	 * getPixel -- get an array of the red, green, and blue values of
	 *   the pixel at row r and column c
	 * DO NOT MODIFY THIS METHOD -- it will be used for grading
	 * @param r The desired row (row 0 is the TOP row)
	 * @param c The desired column
	 * @return the RGB values of pixel (r, c)
	 */
	public int[] getPixel(int r, int c)
	{
		int rgb[] = new int[3];
		rgb[0] = bytes[indexRed(r, c)];
		rgb[1] = bytes[indexGreen(r, c)];
		rgb[2] = bytes[indexBlue(r, c)];
		return rgb;
	}
	
	/**
	 * bytesToInt -- convert an array of bytes to an integer
	 * @param b The bytes to convert, given as little-endian
	 * @return the resulting integer
	 */
	public static int bytesToInt(byte[] b)
	{
		if (b.length != INT_SIZE)
			throw new IllegalArgumentException("Byte array must be 4 bytes long");
		int num = 0, k;
		for (k = 0; k < INT_SIZE; k++)
			num |= (b[k] & 0xff) << (8*k);
		return num;
	}
	
	/**
	 * A sample driver -- you will not be graded on this method, so
	 * you may modify it as necessary to test your program 
	 */
	public static void main(String[] args)
	{
		BMPManipulator m = new BMPManipulator("guna.bmp");
		m.removeRed();
		m.flip();
		m.writeImage("guna2.bmp");
	}
	
}
